#ifndef JSON_BTREE_H
#define JSON_BTREE_H
#include <string.h>
#include <stdio.h>
#include <string>

namespace Jure {
std::string& trim(std::string &s, const char* _s)
{
    if (s.empty()) return s;
    s.erase(0,s.find_first_not_of(_s));
    s.erase(s.find_last_not_of(_s) + 1);
    return s;
}
template <typename T> class CStack{
public:
    CStack(int maxSize)
    {
        _maxSize = maxSize;
        _stack = new T[maxSize];
        memset(_stack, 0, sizeof(T)*maxSize);
        _top = -1;
    }
    ~CStack()
    {
        if(_top != -1)
            delete _stack;
        _top = -1;
    }
    int size()
    {
        return _top+1;
    }
    int push(T value)
    {
        if(size() < _maxSize)
            _stack[++_top] = value;
        return size();
    }
    T pop()
    {
        if(size() > 0)
            return _stack[_top--];
        return ((T)0);
    }
    void clear()
    {
        memset(_stack, 0, sizeof(T)*_maxSize);
        _top = -1;
    }
    T* operator*()
    {
        return _stack;
    }
protected:
    T* _stack;
    int _top;
    int _maxSize;
};

typedef struct _JSON{
    struct _JSON* left;
    struct _JSON* right;
    struct _JSON* parent;
    CStack<char*>* pValues;
    // char value[200];
}JSON, *PJSON;

class CJSON
{
public:
    CJSON()
    {
        pJson = NULL;
        pAuto = NULL;
    }
    CJSON(PJSON pjson)
    {
        pAuto = NULL;
        pJson = pjson;
    }
    CJSON(const char* dict)
    {
        Create(dict);
        pAuto = new int(1);

    }
    ~CJSON()
    {
        if(pAuto!=NULL && *pAuto==1)
        {
            delete pAuto;
            pAuto = NULL;
            ReleaseJSON(pJson);
        }
    }
    void Create(const char* dict)
    {
        int i;
        int nLen = strlen(dict);
        CStack<char> stackChar(nLen+1);
        for(i=0; i<nLen; i++)
            if(dict[i]!=' ' && dict[i]!='\n' && dict[i]!='\t')
                stackChar.push(dict[i]);

        char* pDict = *stackChar;
        nLen = stackChar.size();
        if(**stackChar == '{')
        {
            pDict = pDict+1;
            for(i=0; i<nLen; i++)
            {
                if(stackChar.pop() == '}')
                {
                    nLen = stackChar.size() - 1;
                    break;
                }
            }
        }
        pJson = CreateJSON(pDict, nLen);
    }

    void Release()
    {
        ReleaseJSON(pJson);
    }

    operator const char*()
    {
        return value();
    }
    CJSON operator[](char* key)
    {
        return at(key);
    }
    CJSON operator[](int index)
    {
        return at(index);
    }

    CJSON at(char* key)
    {
        char keyExp[100];
        memset(keyExp, 0, 100);
        strcpy(keyExp, key);
        char* pExp = strrchr(keyExp, '/');
        if(pExp == NULL)
            pExp = strrchr(keyExp, '\\');
        if(pExp != NULL)
        {
            *pExp = 0;
            return at(keyExp).at(pExp+1);
        }

        char keyEx[100];
        memset(keyEx, 0, 100);
        keyEx[0] = '\"';
        memcpy(keyEx+1, key, strlen(key));
        strcat(keyEx, "\"");

        PJSON p = pJson;
        while(p!=NULL)
        {
            if(strcmp(CJSON(p).value(), key) == 0 || strcmp(CJSON(p).value(), keyEx) == 0)
            {
                return CJSON(p->left);
            }
            p = p->right;
        }
        return CJSON();
    }

    CJSON at(int index)
    {
        if(pJson==NULL || index<0) return CJSON();

        PJSON p = pJson;
        while(p!=NULL && index--)
            p = p->right;
        return CJSON(p->left);
    }
    std::string std_value(){
        std::string s = this->value();
        return Jure::trim(s,"\"");
    }
    const char*  value()
    {
        if(pJson && pJson->pValues->size()>0)
            return (**pJson->pValues)[0];
        else return NULL;
    };

    const char*  value(int index)
    {
        if(pJson && pJson->pValues->size()>=index+1)
            return (**pJson->pValues)[index];
        else return NULL;
    };

    bool isEnd()
    {
        return (pJson->left == NULL);
    }

    int size()
    {
        int length = 0;
        PJSON p = pJson;

        while(p!=NULL)
        {
            p = p->right;
            length++;
        }

        return length;
    }

    void toString(char* res)
    {
        if(pJson == NULL)
            return ;

        if(this->size() > 1)
        {
            if(pJson->parent == NULL || pJson->parent->left == pJson)
                strcat(res, "{");
                // printf("{");
        }

        if(pJson->pValues->size() > 1)
        {
            strcat(res, "[");
            // printf("[");
            for(int i=0; i<pJson->pValues->size(); i++)
            {
                strcat(res, this->value(i));
                // printf(this->value(i));
                if(i < pJson->pValues->size() - 1)
                    strcat(res, ",");
                    // printf(",");
            }
            strcat(res, "]");
            // printf("]");
        }
        else
            strcat(res, this->value());
        //	printf("%s", this->value());

        if(pJson->left)
        {
            strcat(res, ":");
            // printf(":");
            CJSON(pJson->left).toString(res);
        }
        if(pJson->right)
        {
            strcat(res, ",");
            // printf(",");
            CJSON(pJson->right).toString(res);
        }
        if(this->size() > 1)
        {
            if(pJson->parent == NULL || pJson->parent->left == pJson)
                strcat(res, "}");
                // printf("}");
        }
    }

    PJSON CreateJSON(char* szDict, int nLen)
    {
        if(nLen <= 0)
            return NULL;

        PJSON pJson = new JSON();
        memset(pJson, 0, sizeof(JSON));
        pJson->pValues = new CStack<char*>(100);
        CStack<char*> stack(nLen+1);
        CStack<char*> stackOperator(10);
        CStack<char> stackChar(nLen+1);

        int i;
        // for(i=0; i<nLen; i++)
        //	printf("%c", szDict[i]);
        // printf("\n");

        int posBegin = 0;
        for(i=0; i<nLen; i++)		// value;
        {
            if(szDict[i] != ':')
            {
                stackChar.push(szDict[i]);
            }
            else if(szDict[i] == ':')
            {
                posBegin = i;
                if(stackChar.size() > 0)
                {
                    setValues(pJson->pValues, *stackChar, stackChar.size()+1);
                //	memcpy(pJson->value, *stackChar, stackChar.size()+1);
                }
                stackChar.clear();
                break;
            }
        }


        if(posBegin == 0)
        {
            setValues(pJson->pValues, szDict, nLen);
            // memcpy(pJson->value, szDict, nLen);
            return pJson;
        }

        int posPoint = posBegin;
        if(szDict[posBegin+1] == '{')	// left;
        {	for (i=posBegin+1; i<nLen; ++i)
            {
                if(szDict[i] == '{')
                {
                    stack.push(szDict+i);
                }
                else if(szDict[i] == '}')
                {
                    char* pos= stack.pop();
                    int nLen = (szDict+i) - (pos);
                    if(stack.size() == 0){
                        posPoint = i;
                        pJson->left = CreateJSON(pos+1, nLen-1);
                        if(pJson->left)pJson->left->parent = pJson;
                        break;
                    }
                }
            }
        }
        else
        {
            for(i=posBegin+1; i<nLen; ++i)
            {
                if(szDict[i] == '[')
                    stackOperator.push(szDict+i);
                else if(szDict[i] == ']')
                    stackOperator.pop();
                if(stackOperator.size() == 0 && szDict[i] == ',')
                {
                    posPoint = i;
                    pJson->left = CreateJSON(szDict+posBegin+1, i-posBegin-1);
                    if(pJson->left)pJson->left->parent = pJson;
                    break;
                }
            }
        }
        if(posPoint == posBegin) // != '{' not hava ','
        {
            pJson->left = CreateJSON(szDict+posBegin+1, nLen-posBegin-1);
            if(pJson->left)pJson->left->parent = pJson;
        }
        else
        {
            if(szDict[posPoint+1] == ',')
                posPoint += 1;
            pJson->right = CreateJSON(szDict+posPoint+1, nLen-posPoint-1);
            if(pJson->right)pJson->right->parent = pJson;
        }

        return pJson;
    } // CreateJSON

    void ReleaseJSON(PJSON pJson)
    {
        if(pJson == NULL)
            return;
        ReleaseJSON(pJson->left);
        ReleaseJSON(pJson->right);

        char* pValue;
        for(int i=0; i<pJson->pValues->size(); i++)
        {
            pValue = pJson->pValues->pop();
            delete pValue;
        }
        delete pJson->pValues;
        delete pJson;
    }
private:
    void setValues(CStack<char*>* pStack, char* str, int nLen)
    {
        if(nLen < 1) return ;

        if(str[0] == '}')
            printf("}");
        CStack<char> stackChar(nLen+1);
        char* pValue = NULL;

        int i;
        // for(i=0; i<nLen; i++)
        //	 printf("%c", str[i]);
        // printf("\n");

        for (i=0; i<nLen; ++i)
        {
            stackChar.push(str[i]);
        }


        if(str[0] == '[')
        {
            stackChar.clear();
            for(i=1; i<nLen-1; i++)
            {
                if(str[i] == ',')
                {
                    pValue = new char[stackChar.size()+8];
                    memset(pValue, 0, stackChar.size()+8);
                    memcpy(pValue, *stackChar, stackChar.size());
                    stackChar.clear();
                    pStack->push(pValue);
                }
                else
                    stackChar.push(str[i]);
            }
        }

        pValue = new char[stackChar.size()+8];
        memset(pValue, 0, stackChar.size()+8);
        memcpy(pValue, *stackChar, stackChar.size());
        stackChar.clear();
        pStack->push(pValue);

    }

private:
    PJSON pJson;
    int * pAuto;
};
} // namespace Jure
int main(int argc, char const *argv[])
{
    // char* dict = "s:{a:{aa: [0,1],ab:1}, b:{ba:2,bb:3}, c:{ca:4, cb:{cba:5, cbb:{cbba:6}}}}";
    char* dict = "{\"202\": {\"peak\": 	{\"value\": 3, \"alarm\": 1, \"unit\": \"dB\"},	\"temperature\": {\"value\": 3, \"alarm\": 0, \"unit\": \"C\"}, \"voltage\":{\"value\": 3, \"alarm\": 0, \"unit\": \"V\"}, \"datetime\": \"2018-12-13\"}, \"203\": {\"peak\": {\"value\": 3, \"alarm\": 1, \"unit\": \"dB\"}, \"temperature\": {\"value\": 3, \"alarm\": 0, \"unit\": \"C\"}, \"datetime\": \"2018-12-13\"}, \"201\": {\"temperature\": {\"value\": 3, \"alarm\": 1, \"unit\": \"C\"}, \"datetime\": \"2018-12-13\"}}";
	// char* dict = "{a:{aa: 0,ab:1}, b:{ba:2,bb:3}, c:{ca:4, cb:{cba:5, cbb:{cbba:6}}}}";
    // char* dict = "{\"errorCode\":\"1\"}";
    FILE* file = fopen("json.txt", "r");
    if(file == NULL)
    {
        printf("not find file: %s\n", "json.txt");
        return 0;
    }
    char fdict[1024];
    memset(fdict, 0, 1024);
    int i=0;
    while((fdict[i] = fgetc(file)) != EOF)
        i++;
    fclose(file);

    char* pDict = dict; // fdict;

    printf("dict: %s\n\n", pDict);

    Jure::CJSON json(pDict);

    // printf("errorCode:%s\n", json.at("errorCode").value());
    printf("202/temperature/unit: %s\n",	json.at("202").at("temperature").at("unit").std_value().c_str());
    printf("201/datetime: %s\n",			json.at("201").at("datetime").std_value().c_str());
    printf("203/0/unit: %s\n",				(const char*)json.at("203").at(0).at("unit"));
    printf("202/2/unit: %s\n",				json["202"][2]["unit"].std_value().c_str());
    printf("201/temperature/unit: %s\n",	json.at("201/temperature/unit").std_value().c_str());

    printf("size: %d \n", json.size());

    char strJson[1024];
    memset(strJson, 0, 1024);
    json.toString(strJson);

    printf("toString: %s\n", strJson);

    return 0;
}
#endif // JSON_BTREE_H
